package czy.demo.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.redis.cache.CacheKeyPrefix;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.*;
import org.springframework.format.support.DefaultFormattingConversionService;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;

/* 启用缓存 */
@EnableCaching
@Configuration
public class SpringRedisCacheConfig {

    private static final Logger logger = LoggerFactory.getLogger(SpringRedisCacheConfig.class);

    @Autowired
    private CacheProperties cacheProperties;

    /* 当配置了RedisCacheConfiguration后，自动配置时就不会使用默认的 */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(){

        CacheProperties.Redis redis = cacheProperties.getRedis();

        /* 获取默认的redis缓存配置 */
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

        /* 键序列化器，使用字符串序列化 */
        config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()));

        /* 值序列化器，使用通用类型的jackson2序列化器 */
        config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));

        /* 缓存存活时间？ */
        if (redis.getTimeToLive() != null) {
            config = config.entryTtl(redis.getTimeToLive());
        }

        /* key前缀 */
        if (redis.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redis.getKeyPrefix());
        }

        /* 是否缓存空值 */
        if (!redis.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }

        /* 是否使用键前缀 */
        if (!redis.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;
    }

    /**
     * 缓存的key生成器
     * key有两种生成方式：
     * 1.直接通过key属性使用spel表达式指定
     * 2.通过KeyGenerator这个Bean来实现
     * 当前的效果是，如果两者都不设置，则使用一种默认的策略生成SimpleKeyGenerator
     * 如果只设置其一，则使用设置的方式
     * 如果两者同时设置,就会出错
     */
    @Bean
    public KeyGenerator keyGenerator(){

        /**
         * 最简单的key生成器，当参数只有一个时，只是按照参数拼凑出key，当参数为多个时，会进行拼接
         * SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator();
        */
        /**
         * 自定义key生成器，可以按照调用对象、方法、参数生成key
         */
        KeyGenerator keyGenerator = new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder builder = new StringBuilder();
                builder.append(target.getClass().getName()+":");
                builder.append(method.getName()+":");
                for(Object object:params){
                    builder.append(object+",");
                }
                return builder.toString();
            }
        };
        return keyGenerator;
    }

    /**
     * 配置缓存错误处理器，当获取、设置、清除三种缓存动作出错时，处理错误
     */
    @Bean
    public CacheErrorHandler cacheErrorHandler(){
        CacheErrorHandler handler = new CacheErrorHandler() {

            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                logger.error("获取缓存出现异常");
                logger.error("缓存名称："+cache.getName()+",缓存key："+key+",异常："+exception.getLocalizedMessage());
            }

            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                logger.error("设置缓存出现异常");
                logger.error("缓存名称："+cache.getName()+",缓存key："+key+",异常："+exception.getLocalizedMessage());
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                logger.error("清除缓存出现异常");
                logger.error("缓存名称："+cache.getName()+",缓存key："+key+",异常："+exception.getLocalizedMessage());
            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                logger.error("清理所有缓存出现异常");
                logger.error("缓存名称："+cache.getName()+",异常："+exception.getLocalizedMessage());
            }
        };
        return handler;
    }

}
